/**
 * this类型
 */
(() => {
    class Counter {
        constructor(public count: number = 0) {
            this.count = count
        }
        public add(count: number): Counter {
            this.count += count
            return this
        }
        public subtract(count: number): Counter {
            this.count -= count
            return this
        }
    }
    const counter1 = new Counter(12)
    console.log(counter1.add(1).subtract(3));

    class PowCounter extends Counter {
        constructor(public count: number = 0) {
            super(count)
        }
        public pow(val: number) {
            this.count = this.count ** val
            return this
        }
    }
    const counter2 = new PowCounter(12)
    // console.log(counter2.add(1).pow(2)); // 在add的时候this的类型变成了Counter，不是PowCounter
    console.log(counter2.pow(2).add(2)); // 没报错
})();


/**
 * 索引类型
 */
(() => {
    // 索引类型查询 // 只能是接口定义的属性名
    interface InfoInterface {
        name: string;
        age: number;
    }
    let infoProp: keyof InfoInterface
    infoProp = "name"
    infoProp = "age"
    // infoProp = "outher" // 报错

    function getValue<T, K extends keyof T>(obj: T, name: K[]) {
        return name.map(n => obj[n])
    } // K只能是T的属性名
    const infoObj = {
        name: 'objName',
        age: 18
    }
    const value: (string | number)[] = getValue(infoObj, ["name", "age"])
    console.log(value);
})();

/**
 * 索引访问操作符 []
 */

(() => {
    interface InfoInterface {
        name: string;
        age: number;
    }
    const data = {
        name: "dataname",
        age: 18
    }
    type NameType = InfoInterface["name"] // 索引访问操作符
    const name: NameType = data.name
})();


(() => {
    const data = {
        name: "dataname",
        age: 18
    }
    function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
        return o[name]
    }
    console.log(getProperty(data, "name"));
})();
(() => {
    interface Objs<T> {
        [key: string]: T
    }
    const obj: Objs<number> = { age: 18 }
    console.log(obj);
    const obj2: Objs<string> = { age: "18" }
    console.log(obj2);

    const keys: Objs<object>["name"] = {}  // 方括号即索引访问符
    console.log(keys);
})();
(() => {
    interface Type {
        a: never
        b: never
        c: string
        d: number
        e: undefined
        f: null
        g: object
    }
    type Test = Type[keyof Type] // 只能是上述类型
})();

(() => {
    /**
     *  映射类型， 用就旧类型创建一个新类型
     */
    interface Info1 {
        age: number
        name: string
        sex: string
    }
    // interface ReadonlyType { // 手动添加
    //     readonly age: number
    //     readonly name: string
    //     readonly sex: string
    // }
    type ReadonlyType<T> = {
        readonly [P in keyof T]?: T[P]
        // +readonly[P in keyof T]+?: T[P] // 默认有加号
    }
    type ReadonlyInfo = ReadonlyType<Info1> // 循环添加readonly属性,和可选属（?）性
    type RmReadonlyType<T> = {
        -readonly [P in keyof T]?: T[P]
    }
    type RmReadonly = RmReadonlyType<Info1>
    let info: ReadonlyInfo = {
        age: 18,
        name: "name",
        sex: "man"
    }
    let rminfo: RmReadonly = info
    // info.age = 19 // 赋值提示错误
    rminfo.age = 19
    console.log("rminfo", rminfo);


    /**
     * 由于该方法比较常用，TS内置了该类型
     * Readonly // 只读
     * Partial // 可选
     */
    type ReadonlyInfo2 = Readonly<Info1> // 循环添加readonly属性,和可选属（?）性
    let info2: ReadonlyInfo2 = {
        age: 18,
        name: "name",
        sex: "man"
    }
})();

(() => {
    /**
     *  其它内置映射类型
     * Pick
     */
    interface Info2 {
        name: string;
        age: number;
        address: string
    }
    let info2: Info2 = {
        age: 18,
        name: "name",
        address: "address"
    }
    function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
        const res: any = {}
        keys.map((key) => {
            res[key] = obj[key]
        })
        return res
    }
    console.log(pick(info2, ["name", "address"]));
    /**
     * Record
     * 将对象中的每个属性值转换为其他值的类型
     */
    function mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U) {
        let res: any = {}
        for (const key in obj) {
            if (obj.hasOwnProperty(key)) {
                res[key] = f(obj[key])
            }
        }
        return res
    }
    const name = {
        0: "hello",
        1: " ",
        2: "world"
    }
    const length = mapObject(name, (s) => s.length)
    console.log(length);
})();

(() => {
    /**
     * 包装与拆包
     */
    type MyProxy<T> = {
        get(): T;
        set(value: T): void
    }
    type MyProxify<T> = {
        [P in keyof T]: MyProxy<T[P]>// 映射类型 通过MyProxy映射的属性值的类型P
    }
    function myProxify<T>(obj: T): MyProxify<T> {
        const result = {} as MyProxify<T>
        for (const key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                result[key] = {
                    get: () => obj[key],
                    set: (value: any) => obj[key] = value
                }
            }
        }
        return result
    }

    let props = {
        name: "lison",
        age: 18
    }
    let proxyProps = myProxify(props)
    proxyProps.name.set("li")
    console.log(proxyProps);
    console.log(props);
    console.log(proxyProps.name.get());

    // 拆包
    function unproxify<T>(t: MyProxify<T>) {
        let res = {} as T
        for (const key in t) {
            if (Object.prototype.hasOwnProperty.call(t, key)) {
                res[key] = t[key].get()
            }
        }
        return res
    }
    console.log(unproxify(proxyProps));

})();

(() => {
    /**
     * keyof 在2.9的升级
     */
    const stringIndex = "a"
    const numberIndex = 1
    const symbolIndex = Symbol()
    type Objs2 = {
        [stringIndex]: string
        [numberIndex]: number
        [symbolIndex]: symbol
    }
    type keysType = keyof Objs2
    const testx: keysType = "a"
    const testx2: keysType = 1
    const testx3: keysType = symbolIndex

    // 类型映射
    type ReadyonlyTypes<T> = {
        readonly [P in keyof T]: T[P]
    }
    let obj3: ReadyonlyTypes<Objs2> = {
        a: "aa",
        1: 11,
        [symbolIndex]: Symbol()
    }
    // obj3.a = "ddd"
    console.log(obj3);
})();

(() => {
    // 元组和数组上的映射类型，会生成新的元组和数组，不会创建新的数据类型
    type MapToPromise<T> = {
        [K in keyof T]: Promise<T[K]>
    }
    type Tuple = [number, string, boolean]
    type promiseTuple = MapToPromise<Tuple>
    let tuple1: promiseTuple = [
        new Promise((resolve, reject) => resolve(1)),
        new Promise((resolve, reject) => resolve("string")),
        new Promise((resolve, reject) => resolve(true)),
    ]
    console.log(tuple1);
})();

/**
 * unknown
 */
(() => {
    // [1] 任何类型都可以复制给unknown
    let value: unknown
    value = "a"
    value = 123

    // [2] 如果没有类型断言或基于控制流的类型细化时，unknown不可以赋值给其他类型，此时他只能赋值给unknown和any类型
    let value2: unknown
    // let value2_1: string = value2 // 报错
    value = value2

    // [3] 如果没有类型断言或基于控制流的类型细化时，不能在他上面进行任何操作
    let value3: unknown
    // value3 += 1 // 报错

    // [4] unknown与任何其他类型组成的交叉类型，最后都等于其他类型
    type type4_1 = string & unknown // string
    type type4_2 = number & unknown // number
    type type4_3 = string[] & unknown // string[]
    type type4_4 = any & unknown // any
    type type4_5 = unknown & unknown // unknown

    // [5] unknown 与任何其他类型（除了any是any）组成的联合类型，都等于unknow
    type type5_1 = string | unknown // unknown
    type type5_2 = any | unknown // any
    type type5_3 = number[] | unknown // unknown

    // [6] never类型是unknown的子类型
    type type6 = never extends unknown ? true : false // true

    // [7] keyof unknown 等于类型 never
    type type7 = keyof unknown // never

    // [8] 只能对unknown 进行等或不等操作，不能进行其他操作
    console.log(value3 === value2);
    console.log(value3 !== value2);

    // [9] unknown类型的值不能访问它的属性，作为函数调用和作为类创建实例
    let value9: unknown
    // value9.age
    // value9()
    // new value9

    // [10] 使用映射类型时如果遍历的是unknown类型则不会映射任何属性
    type Type10<T> = {
        [P in keyof T]: number
    }
    type type10_1 = Type10<any> // [x: string]: number;
    type type10_2 = Type10<unknown> // {}

})();

/**
 * 条件类型
 */
(() => {
    // T extends U ? X : Y

    // [1]
    type Type<T> = T extends string ? string : number
    let index: Type<"a"> = "b" // type string
    let index2: Type<number> = 1 // type number
    let index3: Type<false> = 1 // type number
    console.log(index, index2);

    type TypeName1<T> = T extends any ? T : never
    type Type3 = TypeName1<string | number>

    // [2]
    type TypeNmae<T> =
        T extends string ? string :
        T extends number ? number :
        T extends boolean ? boolean :
        T extends undefined ? undefined :
        T extends () => void ? () => void :
        object
    type Type4 = TypeNmae<() => void> // () => void
    type Type5 = TypeNmae<string[]> // object
    type Type6 = TypeNmae<(() => void) | string[]> //  object | (() => void)

    // [3] TS已经内置，名字Exclude
    type Diff<T, U> = T extends U ? never : T // 判断前面的类型是否是后面类型的子类型
    type Test = Diff<string | number | boolean, undefined | number> // string | boolean
    type Test2 = Exclude<string | number | boolean, undefined | number> // string | boolean

    // [4]
    type Type7<T> = {
        [K in keyof T]: T[K] extends Function ? K : never
    }[keyof T] // [keyof T]取不为never的属性名

    interface Part {
        id: number,
        name: string,
        subparts: Part[],
        undatePart(newName: string): void
    }
    type Test1 = Type7<Part> // type Test1 = "undatePart"

})();

/**
 * 条件类型 infer 关键字用来推断类型
 */
(() => {
    // example 1：如果是一个数组、则返回每一个元素的类型，如果不是则返回元素本身的类型

    // 不使用infer的例子
    type Tpye8<T> = T extends any[] ? T[number] : T
    type Test2 = Tpye8<string[]> // string
    type Test3 = Tpye8<string> // string
    // infer 的例子 这里有一个tslint的报错，，暂时不知道怎么处理
    // tslint:disable-next-line: array-type
    type Type9<T> = T extends Array<infer U> ? U : T
    type Test5 = Type9<string[]> // type Test5 = string
    type Test6 = Type9<string> // type Test6 = string
})();

/**
 * 内置条件类型
 */
(() => {
    // Exclude<T,U> // 从T中剔除U
    type Type10 = Exclude<"a" | "b" | "c", "a"> // type Type10 = "b" | "c"

    // Extract<T,U> // 选出T中可以赋值给C的类型
    type Type11 = Extract<'a' | 'b' | 'c', 'c' | 'b'> // type Type11 = "b" | "c"

    // NonNullable<T> // 剔除T中空类型 null | undefined | never
    // 注意不包含void
    type Type12 = NonNullable<string | number | null | undefined | never | void> // type Type12 = string | number | void

    // ReturnType<T> 获取函数类型返回值类型
    type Type13 = ReturnType<() => string> // string
    type Type14 = ReturnType<() => number> // number
    type Type15 = ReturnType<() => void> // void

    // InstanceType<T> 获取构造函数的返回的实例的类型。
    class AClass {
        constructor() { }
    }
    const a = typeof AClass
    console.log(a);

    type T0 = InstanceType<typeof AClass> // type T1 = AClass
    type T1 = InstanceType<typeof AClass> // type T1 = AClass
    type T2 = InstanceType<any> // type T2 = any
    type T3 = InstanceType<never> // type T3 = never
    // type T4 = InstanceType<string> // 报错，没有构造函数
})();